//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Manageability.Adm;
using EntLibContrib.Resource;
using EntLibContrib.Resource.Configuration;

namespace EntLibContrib.Resource.Configuration.Manageability
{
	/// <summary>
	/// <para>This type supports the Enterprise Library Manageability Extensions infrastructure and is not intended to 
	/// be used directly from your code.</para>
	/// Represents the behavior required to provide Group Policy updates and to publish the <see cref="ConfigurationSetting"/> 
	/// instances associated to the configuration information for the Resource Application Block, and it also manages
	/// the creation of the ADM template categories and policies required to edit Group Policy Objects for the block.
	/// </summary>
	/// <remarks>
	/// This class performs the actual Group Policy update and Wmi object generation for the <see cref="ResourceManagerSettings"/>
	/// configuration section and the <see cref="ResourceManagerData"/> instances contained by it. Processing for 
	/// <see cref="ResourceProviderData"/> instances is delegated to 
	/// <see cref="ConfigurationElementManageabilityProvider"/> objects registered to the configuration object data types.
	/// <para>
	/// The Group Policy directives for the Resource Application Block differ from other block's directives in that policies are
	/// only generated for resource managers, and these policies contain the parts used to override the settings for the
	/// ResourceProviderData instance identified by the <see cref="ResourceManagerData.ResourceProvider">ResourceManagerData.ResourceProvider</see> 
	/// property instance, if any. Manageability providers registered for ResourceProviderData 
	/// subclasses must not generate policies, and the parts they generate must include the
	/// corresponding key name, as they will be included in the resource managers' policies. The purpose for this policy structure
	/// is to make the experience of editing a Group Policy Object's policies for the Resource Application Block similar to that
	/// of the Enterprise Library Configuration Console.
	/// </para>
	/// </remarks>
	/// <seealso cref="ConfigurationSectionManageabilityProvider"/>
	/// <seealso cref="ConfigurationElementManageabilityProvider"/>
	public sealed class ResourceManagerSettingsManageabilityProvider
		: ConfigurationSectionManageabilityProviderBase<ResourceManagerSettings>
	{
		#region Constants
		internal const String DefaultResourceManagerPropertyName = "defaultResourceManager";
		internal const String ResourceProvidersKeyName = "resourceProviders";
		internal const String ResourceManagersKeyName = "resourceManagers";
		#endregion

		#region Properties
		/// <summary>
		/// Gets the name of the category that represents the whole configuration section.
		/// </summary>
		protected override string SectionCategoryName
		{
			get { return InternalResources.ResourceSectionCategoryName; }
		}

		/// <summary>
		/// Gets the name of the managed configuration section.
		/// </summary>
		protected override string SectionName
		{
			get { return ResourceManagerSettings.SectionName; }
		}
		#endregion

		#region Construction
		/// <summary>
		/// <para>This method supports the Enterprise Library Manageability Extensions infrastructure and is not intended to 
		/// be used directly from your code.
		/// </para>
		/// Initializes a new instance of the <see cref="ResourceManagerSettingsManageabilityProvider"/> class with a 
		/// given set of manageability providers to use when dealing with the configuration for resource providers.
		/// </summary>
		/// <param name="subProviders">The mapping from configuration element type to
		/// <see cref="ConfigurationElementManageabilityProvider"/>.</param>
		public ResourceManagerSettingsManageabilityProvider(IDictionary<Type, ConfigurationElementManageabilityProvider> subProviders)
			: base(subProviders)
		{ }
		#endregion

		#region Protected Methods
		/// <summary>
		/// <para>This method supports the Enterprise Library Manageability Extensions infrastructure and is not intended to 
		/// be used directly from your code.</para>
		/// Adds the ADM instructions that describe the policies that can be used to override the configuration
		/// information for the Resource Application Block.
		/// </summary>
		/// <seealso cref="ConfigurationSectionManageabilityProvider.AddAdministrativeTemplateDirectives(AdmContentBuilder, ConfigurationSection, IConfigurationSource, String)"/>
		protected override void AddAdministrativeTemplateDirectives(AdmContentBuilder contentBuilder,
			ResourceManagerSettings configurationSection,
			IConfigurationSource configurationSource,
			String sectionKey)
		{
			AddAdministrativeTemplateDirectivesForSection(contentBuilder, configurationSection, sectionKey);
			AddAdministrativeTemplateDirectivesForResourceManagers(contentBuilder, configurationSection, configurationSource, sectionKey);
		}

		/// <summary>
		/// Overrides the <paramref name="configurationSection"/>'s properties with the Group Policy values from 
		/// the registry.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="policyKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides.</param>
		protected override void OverrideWithGroupPoliciesForConfigurationSection(ResourceManagerSettings configurationSection,
			IRegistryKey policyKey)
		{
			String defaultResourceManagerOverride = policyKey.GetStringValue(DefaultResourceManagerPropertyName);

			configurationSection.DefaultResourceManager = defaultResourceManagerOverride;
		}

		/// <summary>
		/// Creates the <see cref="ConfigurationSetting"/> instances that describe the <paramref name="configurationSection"/>.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="wmiSettings">A collection to where the generated WMI objects are to be added.</param>
		protected override void GenerateWmiObjectsForConfigurationSection(ResourceManagerSettings configurationSection,
			ICollection<ConfigurationSetting> wmiSettings)
		{
			wmiSettings.Add(new ResourceBlockSetting(configurationSection.DefaultResourceManager));
		}

		/// <summary>
		/// Overrides the <paramref name="configurationSection"/>'s configuration elements' properties 
		/// with the Group Policy values from the registry, if any, and creates the <see cref="ConfigurationSetting"/> 
		/// instances that describe these configuration elements.
		/// </summary>
		/// <param name="configurationSection">The configuration section that must be managed.</param>
		/// <param name="readGroupPolicies"><see langword="true"/> if Group Policy overrides must be applied; otherwise, 
		/// <see langword="false"/>.</param>
		/// <param name="machineKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides for the 
		/// configuration section at the machine level, or <see langword="null"/> 
		/// if there is no such registry key.</param>
		/// <param name="userKey">The <see cref="IRegistryKey"/> which holds the Group Policy overrides for the 
		/// configuration section at the user level, or <see langword="null"/> 
		/// if there is no such registry key.</param>
		/// <param name="generateWmiObjects"><see langword="true"/> if WMI objects must be generated; otherwise, 
		/// <see langword="false"/>.</param>
		/// <param name="wmiSettings">A collection to where the generated WMI objects are to be added.</param>
		protected override void OverrideWithGroupPoliciesAndGenerateWmiObjectsForConfigurationElements(ResourceManagerSettings configurationSection,
			bool readGroupPolicies, IRegistryKey machineKey, IRegistryKey userKey,
			bool generateWmiObjects, ICollection<ConfigurationSetting> wmiSettings)
		{
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManagers(configurationSection.ResourceManagers,
				readGroupPolicies, machineKey, userKey,
				generateWmiObjects, wmiSettings);
			OverrideWithGroupPoliciesAndGenerateWmiObjectsForElementCollection(configurationSection.ResourceProviders,
				ResourceProvidersKeyName,
				readGroupPolicies, machineKey, userKey,
				generateWmiObjects, wmiSettings);
		}
		#endregion

		#region Private Methods
		private void AddAdministrativeTemplateDirectivesForSection(AdmContentBuilder contentBuilder,
			ResourceManagerSettings configurationSection,
			String sectionKey)
		{
			contentBuilder.StartPolicy(InternalResources.ResourceManagerSettingsPolicyName, sectionKey);
			{
				contentBuilder.AddDropDownListPartForNamedElementCollection<ResourceManagerData>(InternalResources.ResourceManagerSettingsDefaultResourceManagerPartName,
					DefaultResourceManagerPropertyName,
					configurationSection.ResourceManagers,
					configurationSection.DefaultResourceManager,
					false);
			}
			contentBuilder.EndPolicy();
		}

		/// <devdoc>
		/// ADM templates for resources are different from the other blocks' templates to match the configuration console's 
		/// user experience. Instead of having separate categories with policies for resource managers and resource providers,
		/// the policy for a resource manager includes the parts for its resource provider.
		/// </devdoc>
		private void AddAdministrativeTemplateDirectivesForResourceManagers(AdmContentBuilder contentBuilder,
			ResourceManagerSettings configurationSection,
			IConfigurationSource configurationSource,
			String sectionKey)
		{
			String resourceManagersKey = sectionKey + @"\" + ResourceManagersKeyName;
			String resourceProvidersKey = sectionKey + @"\" + ResourceProvidersKeyName;

			contentBuilder.StartCategory(InternalResources.ResourceManagersCategoryName);
			{
				foreach (ResourceManagerData resourceManagerData in configurationSection.ResourceManagers)
				{
					String resourceManagerPolicyKey = resourceManagersKey + @"\" + resourceManagerData.Name;

					contentBuilder.StartPolicy(String.Format(CultureInfo.CurrentCulture, InternalResources.ResourceManagerPolicyNameTemplate, resourceManagerData.Name), resourceManagerPolicyKey);
					{
						// append the resource manager's provider parts
						contentBuilder.AddTextPart(InternalResources.ResourceProviderSettingsPartName);
						ResourceProviderData resourceProviderData = configurationSection.ResourceProviders.Get(resourceManagerData.ResourceProvider);
						ConfigurationElementManageabilityProvider resourceProviderDataManageabilityProvider = GetSubProvider(resourceProviderData.GetType());
						AddAdministrativeTemplateDirectivesForElement<ResourceProviderData>(contentBuilder,
							resourceProviderData, resourceProviderDataManageabilityProvider,
							configurationSource,
							resourceProvidersKey);
					}
					contentBuilder.EndPolicy();
				}
			}
			contentBuilder.EndCategory();
		}

		private void OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManagers(NamedElementCollection<ResourceManagerData> resourceManagers,
			bool readGroupPolicies, IRegistryKey machineKey, IRegistryKey userKey,
			bool generateWmiObjects, ICollection<ConfigurationSetting> wmiSettings)
		{
			List<ResourceManagerData> elementsToRemove = new List<ResourceManagerData>();

			IRegistryKey machineResourceManagersKey = null;
			IRegistryKey userResourceManagersKey = null;

			try
			{
				LoadRegistrySubKeys(ResourceManagersKeyName,
					machineKey, userKey,
					out machineResourceManagersKey, out userResourceManagersKey);

				foreach (ResourceManagerData data in resourceManagers)
				{
					IRegistryKey machineResourceManagerKey = null;
					IRegistryKey userResourceManagerKey = null;

					try
					{
						LoadRegistrySubKeys(data.Name,
							machineResourceManagersKey, userResourceManagersKey,
							out machineResourceManagerKey, out userResourceManagerKey);

						if (!OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManager(data,
								readGroupPolicies, machineResourceManagerKey, userResourceManagerKey,
								generateWmiObjects, wmiSettings))
						{
							elementsToRemove.Add(data);
						}
					}
					finally
					{
						ReleaseRegistryKeys(machineResourceManagerKey, userResourceManagerKey);
					}
				}
			}
			finally
			{
				ReleaseRegistryKeys(machineResourceManagersKey, userResourceManagersKey);
			}

			foreach (ResourceManagerData data in elementsToRemove)
			{
				resourceManagers.Remove(data.Name);
			}
		}

		private bool OverrideWithGroupPoliciesAndGenerateWmiObjectsForResourceManager(ResourceManagerData data,
			bool readGroupPolicies, IRegistryKey machineKey, IRegistryKey userKey,
			bool generateWmiObjects, ICollection<ConfigurationSetting> wmiSettings)
		{
			if (readGroupPolicies)
			{
				IRegistryKey policyKey = machineKey != null ? machineKey : userKey;
				if (policyKey != null)
				{
					if (policyKey.IsPolicyKey && !policyKey.GetBoolValue(PolicyValueName).Value)
					{
						return false;
					}
				}
			}
			if (generateWmiObjects)
			{
				wmiSettings.Add(new ResourceManagerSetting(data.Name, data.ResourceProvider));
			}

			return true;
		}
		#endregion
	}
}
